from fontTools.ttLib import TTFont  # 处理WOFF字体文件
from PIL import Image, ImageDraw, ImageFont,ImageEnhance  # 绘图
import ddddocr, base64  # 图像识别
from io import BytesIO  # 内存中的字节流处理
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import csv
import re
import httpx
import os
import time


# 基础 URL，移除页码参数
base_url = "https://www.tansoole.com/search/search.htm?gloabSearchVo.queryString=&gloabSearchVo.type=0&gloabSearchVo.listType=1&gloabSearchVo.rootId=1455&gloabSearchVo.segamentId=1604&gloabSearchVo.branchId=&gloabSearchVo.brand=&gloabSearchVo.sortField=cn_len&gloabSearchVo.asc=true&l=&gloabSearchVo.isPro=&gloabSearchVo.clearancePromotion=&tf=&pro=&proTitle=&token=&gloabSearchVo.rkey=kMrlXnaYYM7Q349&gloabSearchVo.fontClassName=font5f5a70be&page.pageSize=300&gloabSearchVo.actId="

# 定义起始页和结束页
start_page = 1
end_page = 254  # 可根据实际情况修改

# 生成多页 URL
start_urls = [f"{base_url}&page.currentPageNo={page}" for page in range(start_page, end_page + 1)]

# 配置 Chrome 浏览器
chrome_options = Options()
# 如果需要无头模式，可以取消注释下面这行
# chrome_options.add_argument("--headless")
service = Service("D:\Driver\chromedriver.exe")  # 请替换为你的 chromedriver 实际路径
driver = webdriver.Chrome(service=service, options=chrome_options)

def font_to_img(txt, filename):
    '''
    将字体画成图片
    :param txt:
    :param filename:
    :return:
    '''
    # 增大图片尺寸
    img_size = 4096
    img = Image.new('1', (img_size, img_size), 255)
    draw = ImageDraw.Draw(img)
    # 适当减小字体大小
    font = ImageFont.truetype(filename, int(img_size * 0.6))
    txt = chr(txt)
    bbox = draw.textbbox((0, 0), txt, font=font)
    x, y = bbox[2] - bbox[0], bbox[3] - bbox[1]
    # 增加字符绘制的边距
    margin = 200
    draw.text(((img_size - x) // 2.8 + margin, (img_size - y) // 2.8+ margin), txt, font=font, fill=0)
    
    # 将图像模式转换为 L（灰度模式）以进行对比度增强
    img = img.convert('L')
    # 增强对比度
    enhancer = ImageEnhance.Contrast(img)
    img = enhancer.enhance(2.0)  # 对比度增强 2 倍
    # 将图像转换回 1 位模式
    img = img.convert('1')
    return img




def font_analysis(filename):
    '''
    传入字体文件名称就能直接出来映射对照
    :param filename:
    :return:
    '''
    analysis_res = []
    try:
        f = TTFont(filename)
        ocr = ddddocr.DdddOcr()
        font_map = {}
        for capcode, Glyphname in f.getBestCmap().items():
            pil = font_to_img(capcode, filename)
            bytes_io = BytesIO()
            pil.save(bytes_io, format="PNG")
            char = ocr.classification(bytes_io.getvalue())
            # 若 char 为空字符串，将其设置为小数点
            if not char:
                char = '.'

            pil.save(f'./imgs/{char}.png', 'png')

            hex_code = f"{capcode:04x}"
            unicode_char = chr(capcode)
            font_map[hex_code] = char
            # print(f"分析结果: {hex_code}-{Glyphname} -> {char}")
        print(font_map)
        return font_map
    except Exception as e:
        print(f"解析字体文件 {filename} 时出错: {e}")
        return {}



def get_font_file_path(driver, url):
    woff_urls = []
    try:
        # 假设经过测试，页面元素平均加载时间为 5 秒，设置超时时间为 8 秒
        wait = WebDriverWait(driver, 8)
        wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'body')))
    except Exception as e:
        print(f"等待页面加载时出错: {e}")
    page_source = driver.page_source
    # 修改正则表达式，匹配包含查询参数的 woff 文件 URL
    matches = re.findall(r'url\(["\']?([^"\')]+?\.woff(?:\?[^"\')]*)?)["\']?\)', page_source, re.IGNORECASE)
    woff_urls.extend(matches)

    if not woff_urls:
        print("未从网页中找到 woff 字体文件的 URL")
        return None

    # 取第一个匹配到的 woff 文件 URL
    font_url = woff_urls[0]
    print(f"获取到的字体文件 URL: {font_url}")

    # 处理相对 URL
    if not font_url.startswith(('http:', 'https:')):
        base_url = url.rsplit('/', 1)[0]
        font_url = f"{base_url}/{font_url.lstrip('/')}"

    # 从 URL 中提取关键标识用于重命名
    match = re.search(r'/font/([^/]+)/font/', font_url)
    if match:
        font_identifier = match.group(1)
        font_file_name = f"tansoole_{font_identifier}.woff"
    else:
        font_file_name = "tansoole.woff"

    # 下载字体文件到本地
    print("开始下载字体文件...")
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36",
        "Referer": url  # 添加 Referer 头，表明请求来源
    }
    try:
        with httpx.Client(headers=headers) as client:
            response = client.get(font_url, timeout=10)
            response.raise_for_status()  # 检查请求是否成功

        # 确保保存目录存在
        save_dir = './fonts/'
        if not os.path.exists(save_dir):
            os.makedirs(save_dir)

        font_file_path = os.path.join(save_dir, font_file_name)
        with open(font_file_path, 'wb') as f:
            f.write(response.content)
        print(f"字体文件已成功保存到 {font_file_path}")
        return font_file_path
    except httpx.RequestError as e:
        print(f"下载字体文件时网络请求出错: {e}")
    except Exception as e:
        print(f"获取字体文件 URL 或保存文件时出错: {e}")
    return None

# 定义统一的 CSV 文件路径
csv_file_path = '合成原料.csv'
# 判断文件是否存在，不存在则创建并写入表头
file_exists = os.path.isfile(csv_file_path)
with open(csv_file_path, 'a', newline='', encoding='utf-8-sig') as csv_file:
    writer = csv.writer(csv_file)
    if not file_exists:
        headers = ['名称', 'CAS号', 'MDL号', '分子式', '分子量', '产品编号', '原商品编号', '品牌', '规格', '参数-指标', '等级', '市场价', '探索价', '货期', '货期备注']
        writer.writerow(headers)

try:
    for page_num, url in enumerate(start_urls, start=start_page):
        driver.get(url)
        # 适当缩短延迟加载时间
        time.sleep(1)
        # 缩短等待时间到 8 秒
        wait = WebDriverWait(driver, 8)
        wait.until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, '.product-show  > div')))

        # 获取当前页面的字体文件路径并解析
        font_file_path = get_font_file_path(driver, url)
        if font_file_path:
            font_map = font_analysis(font_file_path)
        else:
            font_map = {}

        # 获取所有产品框
        product_boxes = driver.find_elements(By.CSS_SELECTOR, '.product-show  > div')

        page_products = []

        for box in product_boxes:
            product = {}

            # 提取产品名称
            try:
                name_element = box.find_element(By.CSS_SELECTOR, 'li.name')
                product['名称'] = name_element.text
            except Exception:
                product['名称'] = ''

            # 提取 CAS 号
            try:
                cas_element = box.find_element(By.CSS_SELECTOR, 'li:nth-of-type(3) span:nth-of-type(1)')
                product['CAS号'] = cas_element.text
            except Exception:
                product['CAS号'] = ''

            # 提取 MDL 号
            try:
                mdl_element = box.find_element(By.CSS_SELECTOR, 'span:nth-of-type(2)')
                product['MDL号'] = mdl_element.text
            except Exception:
                product['MDL号'] = ''

            # 提取分子式
            try:
                formula_element = box.find_element(By.CSS_SELECTOR, 'span:nth-of-type(3)')
                product['分子式'] = formula_element.text
            except Exception:
                product['分子式'] = ''

            # 提取分子量
            try:
                weight_element = box.find_element(By.CSS_SELECTOR, 'span:nth-of-type(4)')
                product['分子量'] = weight_element.text
            except Exception:
                product['分子量'] = ''

            # 提取包装相关信息
            product['包装信息'] = []
            wrapper_elements = box.find_elements(By.CSS_SELECTOR, 'ul.show-list-con')

            for wrapper_element in wrapper_elements:
                package_info = {}

                # 提取产品编号
                try:
                    sku_element = wrapper_element.find_element(By.CSS_SELECTOR, '.row04 a')
                    package_info['产品编号'] = sku_element.text
                except Exception:
                    package_info['产品编号'] = ''

                # 提取原商品编号
                try:
                    original_sku_element = wrapper_element.find_element(By.CSS_SELECTOR, 'li.row10[title]')
                    package_info['原商品编号'] = original_sku_element.text
                except Exception:
                    package_info['原商品编号'] = ''

                # 提取品牌
                try:
                    brand_element = wrapper_element.find_element(By.CSS_SELECTOR, 'li:nth-of-type(3)')
                    package_info['品牌'] = brand_element.text
                except Exception:
                    package_info['品牌'] = ''

                # 提取规格
                try:
                    spec_element = wrapper_element.find_element(By.CSS_SELECTOR, 'li.attrrow15')
                    package_info['规格'] = spec_element.text
                except Exception:
                    package_info['规格'] = ''

                # 提取参数-指标
                try:
                    parameter_element = wrapper_element.find_element(By.CSS_SELECTOR, 'li.parameterrow15')
                    package_info['参数-指标'] = parameter_element.text
                except Exception:
                    package_info['参数-指标'] = ''

                # 提取等级
                try:
                    grade_element = wrapper_element.find_element(By.CSS_SELECTOR, 'li:nth-of-type(6)')
                    package_info['等级'] = grade_element.text
                except Exception:
                    package_info['等级'] = ''

                # 提取市场价
                try:
                    market_price_element = wrapper_element.find_element(By.CSS_SELECTOR, 'span.fz-12-b')
                    class_attr = market_price_element.get_attribute('class')
                    font = ''
                    if class_attr:
                        matches = re.findall(r'\bfont\w*\b', class_attr)
                        if matches:
                            font = ' '.join(matches)
                    market_price = market_price_element.text
                    # 将市场价中的字符转换为十六进制并替换
                    market_price = ''.join([font_map.get(f"{ord(c):04x}", c) for c in market_price])
                    package_info['市场价'] = market_price
                except Exception:
                    package_info['市场价'] = ''

                # 提取探索价
                try:
                    explore_price_element = wrapper_element.find_element(By.CSS_SELECTOR, '.colorRed span')
                    explore_price = explore_price_element.text
                    # 将探索价中的字符转换为十六进制并替换
                    explore_price = ''.join([font_map.get(f"{ord(c):04x}", c) for c in explore_price])
                    package_info['探索价'] = explore_price
                except Exception:
                    package_info['探索价'] = ''

                # 提取货期
                try:
                    delivery_element = wrapper_element.find_element(By.CSS_SELECTOR, 'li.row06')
                    package_info['货期'] = delivery_element.text
                except Exception:
                    package_info['货期'] = ''

                # 提取货期备注
                try:
                    delivery_note_element = wrapper_element.find_element(By.CSS_SELECTOR, 'li:nth-of-type(11)')
                    package_info['货期备注'] = delivery_note_element.text
                except Exception:
                    package_info['货期备注'] = ''

                if any(package_info.values()):
                    product['包装信息'].append(package_info)

            page_products.append(product)

        # 爬取完一页数据后，将该页数据写入 CSV 文件
        with open(csv_file_path, 'a', newline='', encoding='utf-8-sig') as csv_file:
            writer = csv.writer(csv_file)
            for product in page_products:
                name = product['名称']
                cas = product['CAS号']
                mdl = product['MDL号']
                formula = product['分子式']
                weight = product['分子量']

                if product['包装信息']:
                    # 遍历包装信息，每条包装信息单独一行
                    for package_info in product['包装信息']:
                        sku = package_info['产品编号']
                        original_sku = package_info['原商品编号']
                        brand = package_info['品牌']
                        spec = package_info['规格']
                        parameter = package_info['参数-指标']
                        grade = package_info['等级']
                        market_price = package_info['市场价']
                        explore_price = package_info['探索价']
                        delivery = package_info['货期']
                        delivery_note = package_info['货期备注']

                        row = [name, cas, mdl, formula, weight, sku, original_sku, brand, spec, parameter, grade, market_price, explore_price, delivery, delivery_note]
                        writer.writerow(row)
                else:
                    # 当没有包装信息时，只写入产品基本信息
                    row = [name, cas, mdl, formula, weight, '', '', '', '', '', '', '', '', '', '']
                    writer.writerow(row)

        print(f"第 {page_num} 页产品信息已成功写入到 {csv_file_path} 文件中。")

finally:
    # 关闭浏览器
    driver.quit()